﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;

namespace SilverlightDragDrop
{
  internal class GhostControl : Control
  {
  }

  internal class DragDrop
  {
    static DragSource source=null;
    static DropTarget currentDragOver = null;
    static bool dragging=false;
    static Popup overlay;
    static GhostControl ghost = null;

    public static bool IsDragging { get { return dragging; } }

    static DragDrop()
    {
      overlay = new System.Windows.Controls.Primitives.Popup();
      overlay.Child = new Canvas();
    }

    public static void StartDrag(DragSource Source, ControlTemplate GhostTemplate)
    {
      source = Source;
      dragging = true;

      if (GhostTemplate != null)
      {
        (overlay.Child as Canvas).Children.Add(ghost = new GhostControl() { Template = GhostTemplate, DataContext = (Source as FrameworkElement).DataContext });
        var root = (Application.Current.RootVisual as UserControl).Content as Panel;
        (overlay.Child as Canvas).Width = overlay.Width = root.ActualWidth;
        (overlay.Child as Canvas).Height = overlay.Height = root.ActualHeight;
      }
    }

    public static void DragMove(Point position)
    {
      // Move the ghost
      if (ghost != null)
      {
        if (!overlay.IsOpen) overlay.IsOpen = true;
        ghost.SetValue(Canvas.LeftProperty, position.X);
        ghost.SetValue(Canvas.TopProperty, position.Y);
      }

      // Arrange the DragEnter and DragLeave events
      var elementsUnderDragPointer = VisualTreeHelper.FindElementsInHostCoordinates(position, (Application.Current.RootVisual as UserControl).Content);
      bool dropTargetFound = false;
      foreach (var element in elementsUnderDragPointer)
      {
        if (element is DropTarget)
        {
          dropTargetFound = true;

          var newdragover = element as DropTarget;
          if (newdragover.Equals(currentDragOver))
          {
            // Same DropTarget, send draggedmove event
            currentDragOver.DraggedMove(source, position);
          }
          else
          {
            if (currentDragOver != null) currentDragOver.DraggedOut(source, position);
            currentDragOver = newdragover;
            currentDragOver.DraggedOver(source, position);
          }
          break;
        }
      }
      if (!dropTargetFound && currentDragOver != null)
      {
        currentDragOver.DraggedOut(source, position);
        currentDragOver = null;
      }

    }

    public static DragSource StopDragging()
    {
      DragSource Source = source;

      source = null;
      currentDragOver = null;
      dragging = false;
      CleanOverlay();

      return Source;
    }

    public static void DragEnded(Point position)
    {
      CleanOverlay();

      bool DragAborted = true;

      var elementsUnderDragPointer = VisualTreeHelper.FindElementsInHostCoordinates(position, (Application.Current.RootVisual as UserControl).Content);
      foreach (var element in elementsUnderDragPointer)
      {
        if (element is DropTarget)
        {
          DragAborted = false;
          break;
        }
      }

      if (DragAborted)
      {
        if (currentDragOver != null)
        {
          currentDragOver.DraggedOut(source, position);
          currentDragOver = null;
        }
        source = null;
        dragging = false;
      }
      // else: the drop ought to be picked up by the MouseEnter event of the DropTarget
    }

    private static void CleanOverlay()
    {
      if (ghost != null)
      {
        overlay.IsOpen = false;
        (overlay.Child as Canvas).Children.Remove(ghost);
        ghost = null;
      }
    }
  }
}
